#pragma once
#include "DXContext.h"
#include "enums.h"
#include "primitives.h"
#include <memory>
#include <vector>
#include "ErrorCodes.h"

namespace DXBase
{
	namespace D2D
	{
		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class NativeGeometry sealed
		{
		internal:
			NativeGeometry(Microsoft::WRL::ComPtr<ID2D1Geometry> pGeom) : mGeom(pGeom) {}
			Microsoft::WRL::ComPtr<ID2D1Geometry> mGeom;
		};

		ref class PathGeometry;
		ref class GeometrySink;
		ref class StrokeStyle;

		[Windows::Foundation::Metadata::WebHostHidden]
		public interface class IGeometry
		{
			NativeGeometry^ GetNativeGeometry();
		};

		// utility class to implement the IGeometry methods (too many of them, not going to implement them on each geometry)
		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class Geometry sealed
		{
			Geometry();
		public:
			static GEOMETRY_RELATION CompareGeometries(IGeometry^ srcGeometry, IGeometry^ inputGeometry);
			static GEOMETRY_RELATION CompareGeometries(IGeometry^ srcGeometry, IGeometry^ inputGeometry, float3x2 inputGeometryTransform);
			static float Length(IGeometry^ srcGeometry);
			[Windows::Foundation::Metadata::DefaultOverload]
			static float Length(IGeometry^ srcGeometry, float flatteningTolerance);
			static float Length(IGeometry^ srcGeometry, float3x2 worldTransform);
			static float Length(IGeometry^ srcGeometry, float3x2 worldTransform, float flatteningTolerance);
			static float Area(IGeometry^ srcGeometry);
			[Windows::Foundation::Metadata::DefaultOverload]
			static float Area(IGeometry^ srcGeometry, float flatteningTolerance);
			static float Area(IGeometry^ srcGeometry, float3x2 worldTransform);
			static float Area(IGeometry^ srcGeometry, float3x2 worldTransform, float flatteningTolerance);
			static bool TryGetPointAtLength(IGeometry^ srcGeometry, float length, float2 *point, float2 *tangent);
			[Windows::Foundation::Metadata::DefaultOverload]
			static bool TryGetPointAtLength(IGeometry^ srcGeometry, float length, float2 *point, float2 *tangent, float flatteningTolerance);
			static bool TryGetPointAtLength(IGeometry^ srcGeometry, float length, float2 *point, float2 *tangent, float3x2 worldTransform);
			static bool TryGetPointAtLength(IGeometry^ srcGeometry, float length, float2 *point, float2 *tangent, float3x2 worldTransform, float flatteningTolerance);
			static bool FillContains(IGeometry^ srcGeometry, float2 p);
			[Windows::Foundation::Metadata::DefaultOverload]
			static bool FillContains(IGeometry^ srcGeometry, float2 p, float flatteningTolerance);
			static bool FillContains(IGeometry^ srcGeometry, float2 p, float3x2 worldTransform);
			static bool FillContains(IGeometry^ srcGeometry, float2 p, float3x2 worldTransform, float flatteningTolerance);
			static bool StrokeContains(IGeometry^ srcGeometry, float2 p, float width, StrokeStyle^ style);
			[Windows::Foundation::Metadata::DefaultOverload]
			static bool StrokeContains(IGeometry^ srcGeometry, float2 p, float width, StrokeStyle^ style, float flatteningTolerance);
			static bool StrokeContains(IGeometry^ srcGeometry, float2 p, float width, StrokeStyle^ style, float3x2 worldTransform);
			static bool StrokeContains(IGeometry^ srcGeometry, float2 p, float width, StrokeStyle^ style, float3x2 worldTransform, float flatteningTolerance);
			static Windows::Foundation::Rect Bounds(IGeometry^ srcGeometry);
			static Windows::Foundation::Rect Bounds(IGeometry^ srcGeometry, float3x2 inputGeometryTransform);
			static Windows::Foundation::Rect WidenedBounds(IGeometry^ srcGeometry, float width, StrokeStyle^ style);
			[Windows::Foundation::Metadata::DefaultOverload]
			static Windows::Foundation::Rect WidenedBounds(IGeometry^ srcGeometry, float width, StrokeStyle^ style, float flatteningTolerance);
			static Windows::Foundation::Rect WidenedBounds(IGeometry^ srcGeometry, float width, StrokeStyle^ style, float3x2 worldTransform);
			static Windows::Foundation::Rect WidenedBounds(IGeometry^ srcGeometry, float width, StrokeStyle^ style, float3x2 worldTransform, float flatteningTolerance);
		};

		public value struct BEZIER_SEGMENT
		{
			float2 p1;
			float2 p2;
			float2 p3;
		};
		public value struct QUADRATIC_BEZIER_SEGMENT
		{
			float2 p1;
			float2 p2;
		};

		public value struct ARC_SEGMENT 
		{
			float2 point;
			Windows::Foundation::Size size;
			float rotationAngle;
			SWEEP_DIRECTION sweepDirection;
			ARC_SIZE arcSize;
		};


		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class GeometrySink sealed
		{
			Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink;
		internal:
			GeometrySink(Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink) : sink(sink) {}
			Microsoft::WRL::ComPtr<ID2D1GeometrySink> GetNative();
		public:
			virtual ~GeometrySink() { Close(); }
			// ID2D1SimplifiedGeometrySink
			property bool IsClosed { bool get() { return sink == nullptr; } }
			void Close();
			void SetSegmentFlags(PATH_SEGMENT flags);
			void BeginFigure(float2 startPoint, FIGURE_BEGIN begin);
			void EndFigure(FIGURE_END figureEnd);
			void AddBeziers(Windows::Foundation::Collections::IIterable<BEZIER_SEGMENT>^ beziers);
			void AddLines(Windows::Foundation::Collections::IIterable<float2>^ points);
			// ID2D1GeometrySink 
			void AddArc(ARC_SEGMENT arc);
			void AddBezier(BEZIER_SEGMENT bezier);
			void AddLine(float2 p);
			void AddQuadraticBezier(QUADRATIC_BEZIER_SEGMENT qbs);
			void AddQuadraticBeziers(Windows::Foundation::Collections::IIterable<QUADRATIC_BEZIER_SEGMENT>^ qbs);
			// from IGeometry
			void CombineGeometries(IGeometry^ srcGeometry, IGeometry^ inputGeometry, COMBINE_MODE combineMode);
			void CombineGeometries(IGeometry^ srcGeometry, IGeometry^ inputGeometry, COMBINE_MODE combineMode, float3x2 inputGeometryTransform);
			void Outline(IGeometry^ srcGeometry);
			[Windows::Foundation::Metadata::DefaultOverload]
			void Outline(IGeometry^ srcGeometry, float flatteningTolerance);
			void Outline(IGeometry^ srcGeometry, float3x2 worldTransform);
			void Outline(IGeometry^ srcGeometry, float3x2 worldTransform, float flatteningTolerance);
			void Simplify(IGeometry^ srcGeometry, GEOMETRY_SIMPLIFICATION_OPTION option);
			[Windows::Foundation::Metadata::DefaultOverload]
			void Simplify(IGeometry^ srcGeometry, GEOMETRY_SIMPLIFICATION_OPTION option, float flatteningTolerance);
			void Simplify(IGeometry^ srcGeometry, GEOMETRY_SIMPLIFICATION_OPTION option, float3x2 worldTransform);
			void Simplify(IGeometry^ srcGeometry, GEOMETRY_SIMPLIFICATION_OPTION option, float3x2 worldTransform, float flatteningTolerance);
			// TODO Tesselate -> Mesh
			void Widen(IGeometry^ srcGeometry, float strokeWidth, StrokeStyle^ style);
			[Windows::Foundation::Metadata::DefaultOverload]
			void Widen(IGeometry^ srcGeometry, float strokeWidth, StrokeStyle^ style, float flatteningTolerance);
			void Widen(IGeometry^ srcGeometry, float strokeWidth, StrokeStyle^ style, float3x2 worldTransform);
			void Widen(IGeometry^ srcGeometry, float strokeWidth, StrokeStyle^ style, float3x2 worldTransform, float flatteningTolerance);
			// ID2D1PathGeometry
			void AddPathGeometry(PathGeometry^ g);
		};

		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class PathGeometry sealed : IGeometry
		{
		internal:
			NativeGeometry^ ngeom;
			Microsoft::WRL::ComPtr<ID2D1PathGeometry> pgeom;
			virtual NativeGeometry^ GetNativeGeometry() = IGeometry::GetNativeGeometry;

		public:
			// will create a new Geometry
			GeometrySink^ Open() { return Open(FILL_MODE::Alternate); }
			GeometrySink^ Open(FILL_MODE fill);
			property UINT32 FigureCount { UINT32 get(); }
			property UINT32 SegmentCount { UINT32 get(); }
		};

		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class RectangleGeometry sealed : IGeometry
		{
		internal:
			NativeGeometry^ ngeom;
			Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> pgeom;
			virtual NativeGeometry^ GetNativeGeometry() = IGeometry::GetNativeGeometry { return ngeom; }
			Windows::Foundation::Rect rect;

		public:
			RectangleGeometry(Windows::Foundation::Rect rect);
			property Windows::Foundation::Rect Rect { Windows::Foundation::Rect get() { return rect; } }
		};

		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class RoundedRectangleGeometry sealed : IGeometry
		{
		internal:
			NativeGeometry^ ngeom;
			Microsoft::WRL::ComPtr<ID2D1RoundedRectangleGeometry> pgeom;
			virtual NativeGeometry^ GetNativeGeometry() = IGeometry::GetNativeGeometry { return ngeom; }
			Windows::Foundation::Rect rect;
			float radX, radY;

		public:
			RoundedRectangleGeometry(Windows::Foundation::Rect rect, float radX, float radY);
			property Windows::Foundation::Rect Rect { Windows::Foundation::Rect get() { return rect; } }
			property float RadiusX { float get() { return radX; } }
			property float RadiusY { float get() { return radY; } }
		};

		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class EllipseGeometry sealed : IGeometry
		{
		internal:
			NativeGeometry^ ngeom;
			Microsoft::WRL::ComPtr<ID2D1EllipseGeometry> pgeom;
			virtual NativeGeometry^ GetNativeGeometry() = IGeometry::GetNativeGeometry { return ngeom; }
			Windows::Foundation::Rect rect;

		public:
			EllipseGeometry(Windows::Foundation::Rect rect);
			property Windows::Foundation::Rect Bounds { Windows::Foundation::Rect get() { return rect; } }
		};

		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class GeometryGroup sealed : IGeometry
		{
		internal:
			NativeGeometry^ ngeom;
			Microsoft::WRL::ComPtr<ID2D1GeometryGroup> pgeom;
			virtual NativeGeometry^ GetNativeGeometry() = IGeometry::GetNativeGeometry { return ngeom; }
			FILL_MODE fill;
			Windows::Foundation::Collections::IIterable<IGeometry^>^ geoms;

		public:
			GeometryGroup(Windows::Foundation::Collections::IIterable<IGeometry^>^ geoms);
			GeometryGroup(FILL_MODE fill,Windows::Foundation::Collections::IIterable<IGeometry^>^ geoms);
			property Windows::Foundation::Collections::IIterable<IGeometry^>^ SourceGeometries { Windows::Foundation::Collections::IIterable<IGeometry^>^ get() { return geoms; } }
			property FILL_MODE FillMode { FILL_MODE get() { return fill; } }
		};

		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class TransformedGeometry sealed : IGeometry
		{
		internal:
			NativeGeometry^ ngeom;
			Microsoft::WRL::ComPtr<ID2D1TransformedGeometry> pgeom;
			virtual NativeGeometry^ GetNativeGeometry() = IGeometry::GetNativeGeometry { return ngeom; }
			float3x2 transform;
			IGeometry^ source;

		public:
			TransformedGeometry(float3x2 transform, IGeometry^ geom);
			property IGeometry^ SourceGeometry { IGeometry^ get() { return source; } }
			property float3x2 Transform { float3x2 get() { return transform; } }
		};
	}
}
